package com.mostka.rpc.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.tools.SimpleJavaFileObject;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.user.rebind.SourceWriter;

public class DynamicJavaFileObject extends SimpleJavaFileObject {
	
	private String packageName;
	private String className;
	private String superClassName = null;
	private String objectType = "public class";
	public boolean isPublic = true;
	private DynamicJavaFileObject instance;

	private List<String> annotations = new ArrayList<String>();
	private Set<String> imports = new LinkedHashSet<String>();
	private Set<String> interfaceNames = new LinkedHashSet<String>();
	
	public DynamicJavaFileObject(String packageName, String className) {
		super(URI.create("string:///" + packageName.replaceAll("\\.", "/") + "/" + className + Kind.SOURCE.extension), Kind.SOURCE);
		this.packageName = packageName;
		this.className = className;
		instance = this;
	}
	
    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
    	return getSource();
    }
    
    public String getSource(){
    	String sep = System.lineSeparator();
    	String sourceCode = "package "+packageName+";"+sep;
    	sourceCode+=sep;
    	
    	String[] it = imports.toArray(new String[imports.size()]);
    	for(int i=0;i<it.length;i++){
    		sourceCode+="import "+it[i]+";"+sep;
    	}
    	sourceCode+=sep;

    	it = annotations.toArray(new String[annotations.size()]);
    	for(int i=0;i<it.length;i++){
    		sourceCode+="@"+it[i]+sep;
    	}
    	
    	sourceCode += objectType+" "+className;
    	if (superClassName!=null){
    		sourceCode+=" extends "+superClassName;
    	}
    	it = interfaceNames.toArray(new String[interfaceNames.size()]);
    	if (it.length>0){
    		sourceCode+=" implements";
    	}
    	for(int i=0;i<it.length;i++){
    		sourceCode+=" "+it[i];
    		if (i<it.length-1){
    			sourceCode+=",";
    		}
    	}
    	sourceCode+="{"+sep;
    	sourceCode+=source+sep;
    	sourceCode+="}";
    	
        return sourceCode ;
    }

	private String source = "";

	public SourceWriter getSourceWriter() {
		return writer;
	}

	public void addAnnotationDeclaration(String declaration) {
		annotations.add(declaration);
	}

	public void addImplementedInterface(String intfName) {
		interfaceNames.add(intfName);
	}

	public void addImport(String typeName) {
		imports.add(typeName);
	}

	public void setSuperclass(String superclassName) {
		superClassName = superclassName;
	}
	
	public void setAsInterface(){
		objectType = (isPublic?"public ":"")+"interface";
	}
	
	public void setAsClass(){
		setAsClass(false);
	}
	
	public void setAsClass(boolean isAbstract){
		objectType = (isPublic?"public ":"")+(isAbstract?"abstract ":"")+"class";
	}

	SourceWriter writer = new SourceWriter() {
		private final String sep = System.lineSeparator();
		private int indent = 0;
		private boolean atStart = true;

		@Override
		public void println(String s, Object... args) {
			println(String.format(s, args));
		}

		@Override
		public void println(String s) {
			print(s + sep);
			atStart = true;
		}

		@Override
		public void println() {
			print(sep);
			atStart = true;
		}

		@Override
		public void print(String s, Object... args) {
			print(String.format(s, args));
		}

		@Override
		public void print(String s) {
			if (atStart) {
				for (int j = 0; j < indent; ++j) {
					source += "\t";
				}
				atStart = false;
			}
			/*String rest = null;
			int i = s.indexOf(sep);
			if (i > -1 && i < s.length() - 1) {
				rest = s.substring(i + 1);
				s = s.substring(0, i + 1);
			}*/

			source += s;

			/*if (rest != null) {
				atStart = true;
				print(rest);
			}*/
		}

		@Override
		public void outdent() {
			--indent;
		}

		@Override
		public void indentln(String s, Object... args) {
			indentln(String.format(s, args));
		}

		@Override
		public void indentln(String s) {
			indent();
			println(s);
			outdent();
		}

		@Override
		public void indent() {
			++indent;
		}

		@Override
		public void endJavaDocComment() {
			throw new NotImplementedException();
		}
		@Override
		public void commit(TreeLogger logger) {

            String genPath = Compiler.getGenPath();
            if (genPath!=null){
                try {
                	String sep = System.getProperty("file.separator");
                    String path = genPath + sep + packageName.replaceAll("\\.", sep+sep);
                    File file = new File(path);
                    file.mkdirs();
                    if (file.exists()){
	                    path+= sep + className + Kind.SOURCE.extension;
	                    file = new File(path);
	                    file.deleteOnExit();
	                    file.createNewFile();
	                    if (file.canWrite()){
		                    System.out.println("generated file: "+file.getAbsolutePath());
		                    FileOutputStream out = new FileOutputStream(file);
		                    out.write(instance.getSource().getBytes());
		                    out.close();
	                    }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
			try {
				Compiler.doCompilation(logger, instance);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void beginJavaDocComment() {
			throw new NotImplementedException();
		}
	};

}
